package ws.jdbc.concurrent.driver.sqlserver.cursor.wrapper;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.concurrent.locks.ReentrantLock;
import ws.jdbc.concurrent._development.DevMode;

/**
 * Abstract implementation of a result set cursor.
 * This cursor provides implementation of common cursor features.
 *
 * @author Wilson Santos, 36228 <wilson.santos@ua.pt>
 */
public abstract class AbstractCursor {

  /** {@link ResultSet} object that this cursor will work upon. */
  protected final ResultSet rs;
  /** Cursor ID. {@code id = 1, 2, 3, ...}. */
  protected int id;
  /** Identification of the cursor that is currently working upon the result set. */
  protected static int currentCursor = -1;
  /** Lock object. It is used both to assert mutual exclusion (locked during the method execution) and lock the object for exclusive access (locked until {@link #unlock()} is called. */
  protected static final ReentrantLock lock = new ReentrantLock();
  /** Cursor if after the last element of the result set. */
  protected static final int AFTER_LAST = Integer.MAX_VALUE;
  /** Cursor if before the first element of the result set. */
  protected static final int BEFORE_FIRST = Integer.MIN_VALUE;
  /** Tells if the object is explicitly locked. */
  protected static boolean islocked = false;
  /** Result set row that this cursor is currently pointing to. */
  protected int currentRow;
  /** Number of created cursors. */
  private static int count = 0;
  

  /**
   * Create new instance.
   *
   * @param rs {@link ResultSet} object that this cursor will work upon.
   */
  protected AbstractCursor(final ResultSet rs) {
    this.rs = rs;
    id = ++count;
  }

  public int getRow() throws SQLException {
    if (DevMode.concurrent_driver_sqlserver_cursor) {
      DevMode.printlog(Integer.toString(id));
    }
    return currentRow;
  }

  public boolean isAfterLast() throws SQLException {
    return currentRow == AFTER_LAST;
  }

  public boolean isBeforeFirst() throws SQLException {
    return currentRow == BEFORE_FIRST;
  }

  public final void lock() {
    lock.lock();
  }

  public synchronized final void unlock() {
    for (int i = 0; i < lock.getHoldCount(); ++i) {
      lock.unlock();
    }
  }

  /**
   * Release all locks if explicit lock is {@code false}, otherwise release all
   * lock except for the explicit one.
   */
  protected synchronized final void unlockUpdate() {
    int lockCount = islocked ? lock.getHoldCount() - 1 : lock.getHoldCount();
    for (int i = 0; i < lockCount; ++i) {
      lock.unlock();
    }
  }

  /**
   * Move to the saved {@code ResultSet} row.
   *
   * @throws SQLException if a database access error occurs;
   *  the result set is closed or the result set type is {@code TYPE_FORWARD_ONLY}.
   */
  protected synchronized final void restoreContext() throws SQLException {
    if (DevMode.concurrent_driver_sqlserver_cursor) {
      DevMode.printlog(Integer.toString(id));
    }

    if (currentCursor != id) {
      rs.absolute(currentRow);

      if (DevMode.concurrent_driver_sqlserver_cursor) {
      DevMode.printlog(Integer.toString(id) + " retrieved context.");
    }
    }
  }
  
  /**
   * Get property.
   *
   * @return cursor ID.
   */
  public int getId() {
    return id;
  }
}
